りおんクロニクル


SQLite × マイグレーション(DB更新)|安全にスキーマ変更する実務ガイド【2026年版】

Home【2026年版】C# / .NET入門と実践ガイド|基礎・業務アプリ開発・SQLite連携まで体系的に解説

業務アプリを長期運用していると、 「列を追加したい」「型を変えたい」「テーブル構造を見直したい」 といったDB更新(マイグレーション)が必ず発生します。

SQLiteは軽量で便利ですが、ALTER TABLEの制約が多く、 安易にスキーマ変更するとデータ破損・不整合・起動不能につながります。

この記事でわかること
・SQLiteのALTER TABLEの限界
・安全なスキーマ変更パターン(リネーム+コピー)
・バージョン管理(PRAGMA user_version)
・アプリ起動時の自動マイグレーション
・C#でのマイグレーション実装例
・業務アプリ向けベストプラクティス

1. SQLiteのALTER TABLEの限界

SQLiteのALTER TABLEは、他のRDBと比べて機能が限定されています。

■ できること

■ できない/やりにくいこと

そのため、本格的なスキーマ変更は「新テーブルを作ってコピー」するのが基本になります。

2. 安全なスキーマ変更パターン(リネーム+コピー方式)

SQLite公式が推奨しているのは、次のような手順です。

  1. 既存テーブルをリネーム
  2. 新しい定義でテーブルを作成
  3. 必要な列だけINSERT INTO ... SELECTでコピー
  4. 旧テーブルを削除
  5. インデックス・トリガーを再作成

■ 例:Usersテーブルの構造を変更する

-- 1. 旧テーブルをリネーム
ALTER TABLE Users RENAME TO Users_old;

-- 2. 新テーブルを作成
CREATE TABLE Users (
    Id      INTEGER PRIMARY KEY,
    Name    TEXT NOT NULL,
    Email   TEXT,
    Age     INTEGER NOT NULL DEFAULT 0
);

-- 3. データコピー(列をマッピング)
INSERT INTO Users (Id, Name, Email, Age)
SELECT Id, Name, Email, 
       COALESCE(Age, 0)  -- 旧テーブルにAgeがない場合など
FROM Users_old;

-- 4. 旧テーブル削除
DROP TABLE Users_old;

-- 5. インデックス再作成(必要に応じて)
CREATE INDEX idx_users_name ON Users(Name);

この方式なら、列削除・型変更・制約変更など ALTER TABLEでは難しい変更も安全に行えます。

3. バージョン管理(PRAGMA user_version)

アプリのバージョンアップに合わせてDBスキーマを更新するには、 DB側にも「バージョン」を持たせる必要があります。

■ user_version の設定

PRAGMA user_version = 1;

■ 現在のバージョン取得

PRAGMA user_version;

この値を使って、 「バージョン0 → 1」「1 → 2」 のように 段階的にマイグレーションを実行します。

4. アプリ起動時の自動マイグレーション

業務アプリでは、インストーラーや配布だけでなく、 アプリ起動時に自動でDB更新できると運用が非常に楽になります。

■ C#での基本フロー

int currentVersion;
using (var con = factory.CreateConnection())
{
    currentVersion = con.ExecuteScalar<int>("PRAGMA user_version;");
}

if (currentVersion < 1)
{
    Migrate_0_to_1();
}
if (currentVersion < 2)
{
    Migrate_1_to_2();
}
// ...

■ マイグレーション関数の例

private void Migrate_0_to_1()
{
    using var con = factory.CreateConnection();
    using var tran = con.BeginTransaction();

    con.Execute(@"
        CREATE TABLE Users (
            Id      INTEGER PRIMARY KEY,
            Name    TEXT NOT NULL
        );
    ", transaction: tran);

    con.Execute("PRAGMA user_version = 1;", transaction: tran);

    tran.Commit();
}

「バージョンごとに関数を分ける」ことで、 後から見ても分かりやすく、テストもしやすくなります。

5. 既存データを壊さないための注意点

■ 1. 既存列の意味を変えない

同じ列名で意味を変えると、古いデータとの整合性が崩れます。

■ 2. NOT NULL + DEFAULT を活用する

新しい列を追加するときは、DEFAULTを設定しておくと安全です。

ALTER TABLE Users ADD COLUMN Age INTEGER NOT NULL DEFAULT 0;

■ 3. 旧データのマッピングを明示的に書く

INSERT INTO ... SELECT で列を明示的に指定する。

■ 4. バックアップを必ず取る

マイグレーション前にDBファイルをコピーしておくのは鉄則。

6. マイグレーションスクリプトの管理方法

スクリプトはコードと一緒にバージョン管理するのが基本です。

■ SQLファイルを読み込んで実行する例

var sql = File.ReadAllText("Migrations/V2_AddEmail.sql");
con.Execute(sql, transaction: tran);

これにより、DB定義の変更履歴が明確になります。

7. マイグレーションとテスト戦略

マイグレーションはテストしてから本番適用が必須です。

自動テスト(xUnitなど)に組み込むと安心です。

8. 業務アプリ向けベストプラクティス

まとめ:SQLiteのマイグレーションは“慎重に・段階的に・自動化”が正解

「とりあえず手作業でテーブルをいじる」 から卒業して、 「安全に・自動で・再現性のあるマイグレーション」 に移行することで、 SQLiteを使った業務アプリの寿命は大きく伸びます。 この記事をベースに、あなたのプロジェクトに最適なマイグレーション戦略を設計してみてください。

前のページ  次のページ